﻿using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Quail
{
    /// <summary>
    /// The TestLogger class
    /// </summary>
    public sealed class TestLogger : IDisposable
    {
        private readonly object _lockObject = new object();
        private readonly List<ITaskFactory> _taskFactories = new List<ITaskFactory>();
        private Task _rootTask;
        private bool _disposed;

        /// <summary>
        /// Gets or sets a value indicating whether the log file should be flushed after every addition
        /// </summary>
        public bool AutoFlush
        {
            get; set;
        }

        /// <summary>
        /// Gets the logger StopWatch.
        /// </summary>
        public Stopwatch Watch
        {
            get; private set;
        }

        /// <summary>
        /// Gets a value indicating whether the logger IsStarted
        /// </summary>
        public bool IsStarted
        {
            get
            {
                return _rootTask != null;
            }
        }

        /// <summary>
        /// Gets the list of task factories
        /// </summary>
        public List<ITaskFactory> TaskFactories
        {
            get
            {
                return _taskFactories;
            }
        }

        /// <summary>
        /// Gets the OverallState of the test Pass/Warning/Fail
        /// </summary>
        public LogStatus OverallState
        {
            get
            {
                return _rootTask == null ? LogStatus.Startup : _rootTask.OverallStatus;
            }
        }

        /// <summary>
        /// Gets or sets the root task object
        /// </summary>
        public Task RootTask
        {
            get
            {
                return _rootTask;
            }

            set
            {
                _rootTask = value;
            }
        }

        /// <summary>
        /// Gets the active task
        /// </summary>
        public Task ActiveTask
        {
            get
            {
                return RootTask.ActiveTask;
            }
        }

        /// <summary>
        /// When the object is to be disposed
        /// </summary>
        public void Dispose()
        {
            if (!_disposed)
            {
                _disposed = true;
                Stop();
                foreach (var factory in _taskFactories)
                    factory.Dispose();
                _taskFactories.Clear();
            }
        }

        /// <summary>
        /// Starts the logging session
        /// </summary>
        /// <param name="session">The test session</param>
        /// <returns>The new root task</returns>
        public Task Start(TestSession session)
        {
            lock (_lockObject)
            {
                Watch = new Stopwatch();
                Watch.Reset();
                Watch.Start();

                _rootTask = new Task(this, session, null, session.SessionName);

                return _rootTask;
            }
        }

        /// <summary>
        /// Stops the logger
        /// </summary>
        public void Stop()
        {
            lock (_lockObject)
            {
                if (Watch != null)
                    Watch.Stop();

                if (_rootTask != null)
                {
                    if (_rootTask.IsActive)
                        _rootTask.Finish();

                    _rootTask.Flush();
                    _rootTask = null;
                }
            }
        }

        /// <summary>
        /// Flushes the log to disk or whatever
        /// </summary>
        public void Flush()
        {
            lock (_lockObject)
            {
                if (_rootTask != null)
                    _rootTask.Flush();
            }
        }

        /// <summary>
        /// if AutoFlush == true, Flush() is called
        /// </summary>
        public void PerformAutoFlush()
        {
            lock (_lockObject)
            {
                if (AutoFlush)
                    Flush();
            }
        }
    }
}
